package cn.touna.jss.modules.third.bee;

import cn.touna.risk.api.enums.SystemErrorCodeEnum;
import cn.touna.jss.common.factory.ResponseFactory;
import cn.touna.jss.common.properties.disconf.BeeProperties;
import cn.touna.jss.common.util.ValidUtil;
import cn.touna.jss.modules.adapter.ApiMapping;
import cn.touna.jss.modules.third.bee.entity.BeeRequestEntity;
import cn.touna.risk.api.domain.Response;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.beeseven.encrypt.EncryptHelper;
import com.beeseven.sdk.call.ServiceCaller;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 大峰接口调用
 * Created by 吴柏平 on 2017/7/31.
 */
@Service
public class BeeService {

    private static final Logger logger = LogManager.getLogger();
    @Autowired
    private BeeProperties beeProperties;

    @Resource(name = "scheduledThredPool")
    private ScheduledExecutorService threadPool;

    private Map<Thread, Response> threadResponseMap = new HashMap<>();

    /**
     * 同步请求大蜂报告<br/>
     * 存在问题，没有使用到该借口
     */
    private Response<Map> syncRequest1(Map jsonObject) {
        String jsonString = JSON.toJSONString(jsonObject);
        logger.info("REQUEST 【大峰数据-同步调用】 json String ：{}", jsonString);
        Response<Map> response = new Response<>();
        try {
            String res = ServiceCaller.request(beeProperties.getClientId(), beeProperties.getAppCode(), jsonString, beeProperties.getPassword(), beeProperties.getSignature());
            logger.info("RESPONSE 【大峰数据-同步调用】 json String ：{}", res);
            return ResponseFactory.success(JSON.parseObject(res));
        } catch (Exception e) {
            logger.error("ERROR【大峰数据-同步调用】 接口调用错误 错误信息：{}", e.getMessage());
            return response.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR);
        }
    }

    /**
     * 发送异步请求
     *
     * @param params
     * @return
     */
    private Response<JSONObject> sendAsyncRequest(String params) {
        try {
            String res = ServiceCaller.asyncRequest(beeProperties.getClientId(), beeProperties.getAppCode(), params, beeProperties.getPassword(), beeProperties.getSignature());
            res = res.replace("\r\n", "").replace(" ", "");
            JSONObject obj = JSONObject.parseObject(res);
            if ("success".equals(obj.getString("status"))) {
                return ResponseFactory.success(obj);
            } else
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, obj.getJSONObject("response").getString("message"), obj);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, e.getMessage());
        }
    }


    /**
     * 获取异步请求的结果
     *
     * @param params
     * @return
     */
    private Response asyncRequestResult(String params) {
        try {
            String res = ServiceCaller.asyncRequestResult(beeProperties.getClientId(), beeProperties.getAppCode(), params, beeProperties.getPassword(), beeProperties.getSignature());
            res = res.replace("\r\n", "").replace(" ", "");
            JSONObject resObj = JSONObject.parseObject(res);
            return ResponseFactory.success(resObj);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, e.getMessage());
        }
    }

    /**
     * 请求大蜂报告<br/>
     * 将异步发送请求和异步获取结果结合在一起，需要等待2~5分钟
     *
     * @param requestObj
     * @return
     */
    @ApiMapping(value = "Bee.asyncRequest", desc = "大蜂.资信查询")
    public Response asyncRequest(JSONObject requestObj) {
        //参数校验
        BeeRequestEntity request = JSON.toJavaObject(requestObj, BeeRequestEntity.class);
        Response response = valid(request);
        if (response != null)
            return response;

        //字段名称匹配
        requestObj.put("personal_name", requestObj.getString("name"));
        requestObj.put("personal_id", requestObj.getString("idCard"));
        requestObj.put("personal_mobile", requestObj.getString("mobile"));
        requestObj.put("personal_id_type", "身份证");

        //发起请求
        response = sendAsyncRequest(requestObj.toJSONString());
        if (response.checkIfFail())
            return response;

        //组装参数
        JSONObject obj = ((JSONObject) response.getData()).getJSONObject("response"); //JSONObject.parseObject((String) response.getData()).getJSONObject("response");
        String transactionId = obj.getString("transaction_id");
        String requestLogId = obj.getString("request_log_id");

        JSONObject jRequest = new JSONObject();
        jRequest.put("transactionId", transactionId);
        jRequest.put("requestLogId", requestLogId);
        //获取请求结果
        response = syncRecursiveGetResult(jRequest.toJSONString());
        return response;
    }

    /**
     * 校验入参，依赖hibernate validator
     *
     * @param request
     * @return
     */
    private Response valid(BeeRequestEntity request) {
        try {
            Response response = ValidUtil.valid(request);
            if (response != null) return response;
            response = validList(request.getBank_cards());
            if (response != null) return response;
            response = validList(request.getCorporations());
            if (response != null) return response;
            response = validList(request.getLife_insurance());
            if (response != null) return response;

            response = validList(request.getLinkman());
            if (response != null) return response;
            response = validList(request.getPersonal_home_contacts());
            if (response != null) return response;
            response = validList(request.getTraffic());
            if (response != null) return response;
            response = validList(request.getVehicle());
            if (response != null) return response;
            response = validList(request.getVillage());
            if (response != null) return response;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return ResponseFactory.fail(SystemErrorCodeEnum.CRS_PARAM_CHECK_FAILED, "请求的格式错误，" + e.getMessage());
        }
        return null;
    }

    private Response validList(List list) {
        if (list == null || list.size() == 0)
            return null;
        for (Object obj : list) {
            Response response = ValidUtil.valid(obj);
            if (response != null)
                return response;
        }
        return null;
    }

    private Response syncRecursiveGetResult(String params) {
        int count = 0;
        long start = System.currentTimeMillis();
        Response response = asyncRequestResult(params);
        JSONObject obj = (JSONObject) response.getData();
        String status = obj.getString("status");
        if ("success".equals(status)) {
            return response;
        } else if ("failed".equals(status)) {
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR,
                    obj.getJSONObject("response").getString("message"), obj);
        } else {
            while (true) {
                try {
                    count++;
                    if (count >= 200) {
                        break;
                    }
                    response = asyncRequestResult(params);
                    logger.info("重新请求大蜂数据,第" + count + "次查询结果:" + response.toString());
                    obj = (JSONObject) response.getData();
                    status = obj.getString("status");
                    if ("success".equals(status)) {
                        break;
                    } else if ("failed".equals(status)) {
                        response = ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, obj.getJSONObject("response").getString("message"), obj);
                    }
                    TimeUnit.SECONDS.sleep(3);
                } catch (Exception e) {
                    logger.error("重新请求大蜂数据时异常", e);
                    response = ResponseFactory.fail(SystemErrorCodeEnum.CRS_SYSTEM_FAILED, e.getMessage());
                    break;
                }

            }
        }
        long cost = System.currentTimeMillis() - start;
        logger.info("华策数据查询消耗时间：" + cost);
        return response;
    }

    /**
     * 定时刷新获取异步请求的结果
     *
     * @param params
     * @return
     */
    private Response recursiveGetResult(String params) {
        long start = System.currentTimeMillis();
        try {
            Response response = asyncRequestResult(params);
            JSONObject obj = (JSONObject) response.getData();
            String status = obj.getString("status");
            CountDownLatch latch = new CountDownLatch(1);
            if ("success".equals(status)) {
                return response;
            } else if ("failed".equals(status)) {
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR,
                        obj.getJSONObject("response").getString("message"), obj);
            } else {
                RequestResultThread requestResultThread = new RequestResultThread(params, Thread.currentThread(), threadResponseMap, latch);
                threadPool.execute(requestResultThread);
                try {
                    latch.await();
                    synchronized (threadResponseMap) {
                        response = threadResponseMap.get(Thread.currentThread());
                        threadResponseMap.remove(Thread.currentThread());
                        return response;
                    }
                } catch (Exception e) {
                    logger.error("大蜂获取查询结果失败", e);
                    return ResponseFactory.fail(SystemErrorCodeEnum.CRS_SYSTEM_FAILED, e.getMessage());
                }
            }
        } catch (Exception e) {
            logger.error("大蜂获取查询结果失败", e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR, e.getMessage());
        } finally {
            long cost = System.currentTimeMillis() - start;
            logger.info("大蜂数据查询消耗时间：" + cost);
        }
    }

    class RequestResultThread implements Runnable {

        private String params;
        private Thread rootThread;
        private Map<Thread, Response> map;
        private CountDownLatch latch;
        private int count = 1;

        public RequestResultThread(String params, Thread rootThread, Map<Thread, Response> map, CountDownLatch latch) {
            this.params = params;
            this.rootThread = rootThread;
            this.map = map;
            this.latch = latch;
        }

        @Override
        public void run() {
            Response response;
            try {
                logger.info("重新请求大蜂数据，第" + String.valueOf(count++) + "次，thread=" + Thread.currentThread().getName());
                response = asyncRequestResult(params);
                JSONObject obj = (JSONObject) response.getData();
                String status = obj.getString("status");
                if ("success".equals(status)) {
                    map.put(rootThread, response);
                    latch.countDown();
                } else if ("failed".equals(status)) {
                    response = ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, obj.getJSONObject("response").getString("message"), obj);
                    map.put(rootThread, response);
                    latch.countDown();
                } else {
                    threadPool.schedule(this, 20, TimeUnit.SECONDS);
                }
            } catch (Exception e) {
                logger.error("重新请求大蜂数据时异常", e);
                response = ResponseFactory.fail(SystemErrorCodeEnum.CRS_SYSTEM_FAILED, e.getMessage());
                map.put(rootThread, response);
                latch.countDown();
            }
        }
    }
}
